package tq.com.tqim.socket;

import android.text.Selection;
import android.util.Log;

import org.json.JSONObject;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketOptions;
import java.net.StandardSocketOptions;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Set;

import tq.com.systools.system.StringHelper;
import tq.com.tqim.socket.message.TQIMMessageBase;
import tq.com.tqim.socket.message.TQIMMessageFactory;
import tq.com.tqim.socket.message.TQIMSMessage;
import tq.com.tqim.socket.message.TQIMSValidMsg;

public class TQIMSocketClient {
    static private String TAG = "TQIMSocketClient";
    static private TQIMSocketClient shareClient = new TQIMSocketClient();
    private TQIMSocketClient(){}
    static public TQIMSocketClient getShareClient(){return shareClient;};


    private String IP;
    private int port = 0;
    private String mToken ;
    public void startService(String ip, int port, String token){
        this.IP = ip;
        this.port = port;

        mToken = token;
        restart();
    }

    private IMThread imThread = null;
    public void restart(){
        if(StringHelper.isEmpty(this.IP) || port == 0){
            return;
        }
        if(mToken == null){
            return;
        }

        if(imThread != null){
            imThread.stopThead();
            imThread = null;
        }

        if(imThread == null){
            imThread = new IMThread();
            imThread.start();
        }
    }

    private InetSocketAddress severAddress(){
        return new InetSocketAddress(this.IP, port);
    }

    private int mStep;
    static final private int STEP_CONNECT = 0x00;
    static final private int STEP_VALID = 0x01;
    static final private int STEP_VALID_WAIT = 0x02;
    static final private int STEP_MESSAGE = 0x03;

    class IMThread extends Thread{
        private boolean mStop = false;
        private SocketChannel socketChannel = null;
        private Selector selector = null;

        private ByteBuffer cacheHeaderBuffer = ByteBuffer.allocate(8);
        private ByteBuffer cacheBodyBuffer;
        private TQIMSMessage cacheMsg ;

        @Override
        public void run() {

            while (!mStop){
                if(socketChannel == null){
                    try {
                        socketChannel = SocketChannel.open();
                        socketChannel.configureBlocking(false);

                        setSocketOpt(socketChannel);
                    }catch (IOException e){
                        e.printStackTrace();
                    }
                }
                if(mStep == STEP_CONNECT){
                    try{
                        if(socketChannel.isConnected()){
                            socketChannel.close();
                        }

                        socketChannel.connect(severAddress());
                        int i = 0;
                        while(!socketChannel.finishConnect() && !mStop){
                            Thread.sleep(1000);
                            Log.d(TAG, String.format("connect to server %d....", i));
                            i++;
                        }
                        mStep = STEP_VALID;
                    }catch (IOException e){
                        e.printStackTrace();

                        continue;
                    }catch (InterruptedException e){
                        e.printStackTrace();
                        continue;
                    }
                }

                switch (mStep){
                    case STEP_VALID:{
                        if(writeValidMsg(socketChannel) == -1){
                            mStep = STEP_CONNECT;
                            continue;
                        }
                    }
                }
                try {
                    //reading
                    if(selector == null){
                        selector = Selector.open();
                        SelectionKey key = socketChannel.register(selector, SelectionKey.OP_READ);
                        if(key.isValid()){
                            Log.d(TAG, "run: ");
                            
                        }
                    }

                    int set = selector.select(200);
                    if(set == 0){
                        continue;
                    }

                    Set<SelectionKey> keys = selector.selectedKeys();
                    if(keys.size() == 0) continue;

                    Iterator<SelectionKey> iterator = keys.iterator();
                    if (iterator.hasNext()){
                        SelectionKey key = iterator.next();
                        if(!key.isReadable()){
                            continue;
                        }
                        iterator.remove();

                        SocketChannel readChannel = (SocketChannel)key.channel();
                        if(!readChannel.isConnected()){
                            mStep = STEP_CONNECT;
                            continue;
                        }

                        int ret = 0;
//                        do{//
                            if(cacheMsg == null || !cacheMsg.hasHeader()){
                                ret = readChannel.read(cacheHeaderBuffer);
                                if(ret == -1){
                                    mStep = STEP_CONNECT;
                                    continue;
                                }
                                if(ret == 0){
                                    continue;
                                }

                                if(cacheHeaderBuffer.position() < TQIMSMessage.MSG_HEADER_LEN){
                                    continue;
                                }

                                cacheHeaderBuffer.flip();
                                cacheMsg = TQIMSMessage.readHeader(cacheHeaderBuffer);
                                if(cacheMsg != null && cacheMsg.hasHeader()){
                                    cacheHeaderBuffer.clear();
                                    continue;
                                }else{
                                    //
                                    closeSocket();
                                    ret = 0;
                                    continue;
                                }
                            }else{
                                if(cacheBodyBuffer == null){
                                    cacheBodyBuffer = ByteBuffer.allocate(cacheMsg.getBodyLen());
                                }

                                ret = readChannel.read(cacheBodyBuffer);
                                if(ret == -1){
                                    mStep = STEP_CONNECT;
                                    continue;
                                }
                                if(ret == 0){
                                    continue;
                                }
                                if(cacheBodyBuffer.position() < cacheMsg.getBodyLen()){
                                    //read
                                    continue;
                                }else{
                                    cacheBodyBuffer.flip();
                                    cacheMsg.setBody(cacheBodyBuffer);
                                    cacheBodyBuffer = null;
                                }

                                if(cacheMsg.hasBody()){
                                    //deal Msg;
                                    onRecvMsg(cacheMsg);
                                    cacheMsg = null;
                                }
                            }
//                        }while(ret > 0);



                        /*ByteBuffer header = ByteBuffer.allocateDirect(8);
                        int ret = readChannel.read(header);
                        if(ret == -1){
                            mStep = STEP_CONNECT;
                            continue;
                        }

                        header.flip();
                        TQIMSMessage msg = TQIMSMessage.readHeader(header);
                        if(msg !=null && msg.hasHeader()){
                            ByteBuffer body = ByteBuffer.allocate(msg.getBodyLen());

                            readChannel.read(body);
                            body.flip();
                            if(body.limit() == msg.getBodyLen()){
                                Charset charset = Charset.forName("UTF-8");
                                String str = charset.decode(body).toString();

                                msg.setBody(str);
                            }else{
                                Log.d(TAG, "need cache body...");
                            }
                        }else{
                            //need cache header
                        }*/
                    }
                }catch (IOException e){
                    e.printStackTrace();
                }
            }


            closeSocket();
        }

        private void closeSocket(){
            try {
                if(selector.isOpen()){
                    selector.close();
                }
                if(socketChannel.isConnected()){
                    socketChannel.close();
                }
            }catch (IOException e){
                e.printStackTrace();
            }
            mStep = STEP_CONNECT;

            selector = null;
            socketChannel = null;
        }

        public void stopThead(){
            mStop = true;
            do{
                try {
                    join(1000);
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
            }while (false);
        }


    }


    private void setSocketOpt(SocketChannel s){
        try {
            s.socket().setTcpNoDelay(true);
            s.socket().setOOBInline(true);
            s.socket().setReceiveBufferSize(512);
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    private int writeValidMsg(SocketChannel socketChannel){
        TQIMSValidMsg msg = new TQIMSValidMsg(mToken);
        try {
            int ret = socketChannel.write(msg.getBuffer());
            mStep = STEP_VALID_WAIT;
            return ret;
        }catch (IOException e){
            e.printStackTrace();
        }
        return -1;
    }

    private void onRecvMsg(TQIMSMessage msg){
        JSONObject object = msg.bodyJson();
        if(object == null){
            return;
        }

        int id = object.optInt("msg_id");
        switch (id){
            case TQIMMessageFactory.MSG_VALID_RET:{
                mStep = STEP_MESSAGE;
                break;
            }
            case TQIMMessageFactory.MSG_COMMON:{
                if(callback != null){
                    callback.onMsg(TQIMMessageFactory.commonMsg(object));
                }
                break;
            }
        }
    }

    public interface IMMessage{
        void onMsg(TQIMMessageBase msg);
    }

    private IMMessage callback;
    public void setCallback(IMMessage callback){
        this.callback = callback;
    }

}
